#pragma once

#include <string>
#include <vector>

enum ELayoutVarType
{
	eLayoutVar_Invalid,
	eLayoutVar_Bool,
	eLayoutVar_Int8,
	eLayoutVar_Int16,
	eLayoutVar_Int32,
	eLayoutVar_Int64,
	eLayoutVar_Uint8,
	eLayoutVar_Uint16,
	eLayoutVar_Uint32,
	eLayoutVar_Uint64,
	eLayoutVar_Float,
	eLayoutVar_Double,
	eLayoutVar_String,
	eLayoutVar_Enum,

	eLayoutVar_Group, // requires sub group
};

/// Layout attribute (simple inline property)
class CLayoutGroupElement
{
public:
	CLayoutGroupElement( const std::string& name, ELayoutVarType varType, const bool isOptional, const class CLayoutGroup* subGroup = nullptr, bool isArray = false );
	~CLayoutGroupElement();

	inline const bool IsOptional() const { return m_isOptional; }

	inline const std::string& GetName() const { return m_name; }
	inline const ELayoutVarType GetType() const { return m_type; }

	inline const std::string& GetDefaultValue() const { return m_default; }
	inline const bool HasDefaultValue() const { return m_hasDefaultValue; }

	inline const class CLayoutGroup* GetSubGroup() const { return m_subGroup; }

	inline bool IsArray() const { return m_isArray; }

private:
	std::string		m_name;
	ELayoutVarType	m_type;

	std::string		m_default;
	bool			m_hasDefaultValue;

	bool			m_isOptional;

	bool			m_isArray;

	const class CLayoutGroup*	m_subGroup;

	friend class CLayoutBuilder;
};

/// Layout description for object
class CLayoutGroup
{
public:
	CLayoutGroup( const std::string& name, const bool isMessage );
	~CLayoutGroup();

	inline const std::string& GetName() const { return m_name; }
	inline const bool IsDefined() const { return m_defined; }

	inline const bool HasDerivecGroups() const { return !m_derivedGroups.empty(); }

	inline const size_t GetNumElements() const { return m_elements.size(); }
	inline const CLayoutGroupElement* GetElement( size_t index ) const { return m_elements[index]; }

	inline const CLayoutGroup* GetBaseGroup() const { return m_baseGroup; }

	inline const ELayoutVarType GetValueType() const { return m_valueType; }

	inline const std::vector< const CLayoutGroup* >& GetDerivedGroups() const { return (const std::vector< const CLayoutGroup* >&) m_derivedGroups; }

	inline const bool IsMessage() const { return m_isMessage; }

	void EnumChildGroups( std::vector<const CLayoutGroup*>& allChildGroups ) const;

	const CLayoutGroup* FindChildGroup( const std::string& groupName ) const;

	bool DerivesFrom( const CLayoutGroup* baseGroup ) const;

	const CLayoutGroupElement* GetElement( const std::string& name ) const;

	inline const int GetClassIndex() const { return m_classIndex; }
	inline const int GetLastChildIndex() const { return m_lastChildIndex; }

	void GetWritableElements( std::vector< const CLayoutGroupElement* >&outElems ) const;

private:
	std::string								m_name;
	bool									m_defined;
	bool									m_isMessage;

	std::vector< CLayoutGroupElement* >		m_elements;

	CLayoutGroup*							m_baseGroup;
	std::vector< CLayoutGroup* >			m_derivedGroups;

	ELayoutVarType							m_valueType;

	int										m_classIndex;
	int										m_lastChildIndex;

	friend class CLayoutBuilder;
	friend class CLayoutHierarchyBuilder;
};

/// Layout definition
class CLayoutDefinition
{
public:
	CLayoutDefinition();
	~CLayoutDefinition();

	// get all groups
	inline const std::vector< const CLayoutGroup* >& GetGroups() const { return (const std::vector< const CLayoutGroup* >&) m_groups; }

	// get group by name
	const CLayoutGroup* FindGroup( const std::string& groupName );

	// validate definitions
	bool Validate() const;

private:
	std::vector< CLayoutGroup* >	m_groups;

	// get/create group with given name
	CLayoutGroup* GetGroup( const std::string& groupName, const bool isMessage );

	friend class CLayoutBuilder;
};


